﻿/****************************************************************************************
 * Copyright © 2020 - 2023 by AngryKeyboard.
 * SFML_俄罗斯方块 is licensed under MulanOWL BY v1.
 * You can use this software according to the terms and conditions of the MulanOWL BY v1.
 * You may obtain a copy of MulanOWL BY v1 at:
 *          http://license.coscl.org.cn/MulanOWLBYv1
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the MulanOWL BY v1 for more details.
****************************************************************************************/
#pragma once
#include "CBlock.h"

class CAutoFill {
public:
  static CBlock *getBestBlock(const CBlock &curBlock, const int table[ROW_COUNT][COL_COUNT]);//自动填充运算，返回最佳方块的动态存储

protected:
  /***************************
  Pierre Dellacherie算法相关接口
  ***************************/
  static double getLandingHeight();   //获取方块落地时，所有点row坐标平均值
  static double getErodedPieceCells();//获取方块落地时，可以用来消行的点的数量
  static double getRowTransitions();  //获取table中，横向空格、方块的边沿个数
  static double getColTransitions();  //获取table中，纵向空格、方块的边沿个数
  static double getHoleSum();         //获取table中，空洞的个数
  static double getWellSum();         //获取table中，空洞的个数
  static void renewValues();          //刷新所有的值
  static double getResultValue();           //获取用公式计算后的值

private:
  /**************
  方块的模拟运动接口
  **************/
  static void fillToButtom(); //把m_TempBlock固化到m_TempTable底部
  static int clearLines();  //扫描m_TempTable，统计被填满的行数
  static void moveToTop();  //把m_TempBlock移动到顶部
  static void rotate(int times = 1);  //把mTempBlock旋转多次

  /***************************
  Pierre Dellacherie算法相关参数
  ***************************/
  //计算公式：
  //m_nLastValue = 45 × landingHeight + 34 × erodedPieceCells - 32 × rowTransitions - 93 × colTransitions - 79 × holeSum - 34 × wellSum
  //权重可以自行调整。

  //公式中每一项的序号（访问weight和values用）
  enum elementIndex {
    landingHeight, erodedPieceCells, rowTransitions, colTransitions, holeSum, wellSum, removeLines,
    elementCount
  };
  //const double weight[elementCount] = { 45, 32, -32, -93, -79, -34 };//各参数的权重
  static double weight[elementCount];    //各参数的权重
  static double values[elementCount];    //每一项的值
  static double m_dMaxResultValue;       //最终结果

  static CBlock m_TempBlock;//运算中用到的方块
  static int m_TempTable[ROW_COUNT][COL_COUNT];//运算中用到的桌面
};



/**************************************************
以下代码和算法来自网络，但是因为方块表示方法不同，代码不适用
**************************************************/

//算法实现从网络获取
//参考链接1：https://blog.csdn.net/qq_41882147/article/details/80005763
//参考链接2：https://blog.csdn.net/Originum/article/details/81570042 （AI代码）


/*
#include <iostream>
using namespace std;

class AI {
public:
    AI(char MAP[100][100], int);
    int LandingHeight;
    int RowsEliminated;
    int RowTransitions();
    int ColumnTransitions();
    int NumberofHoles();
    int WellSums();
    int PierreDellacherie();

    bool collisionJudge(int, int);
    void addTomap(int, int);
    void AIgo(int& X, int& Y, int& shape);

private:
    int maxValue;
    char map[22][12];                        //用来操作
    char copyMap[22][12];                    //记录传入情况
    char cur[4][4];
    int type;//可以传出
};


AI::AI(char MAP[100][100], int TYPE) {     //获得当前局势
    type = TYPE;//传入方块的值
    for (int i = 0; i < 22; i++)
        for (int j = 0; j < 12; j++) {
            if (MAP[i][j] != '#')
                copyMap[i][j] = MAP[i][j];
            else copyMap[i][j] = 0;//空格赋值为0
        }
    maxValue = -0x3f3f3f;
}

//碰撞检测只需要检测向下移动时的状态（由于变形是被枚举，左右移动不是用户操作的，一开始一定水平先移动到最优位置）,用传入地图
bool AI::collisionJudge(int x, int y) {
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
        {
            int tx = x + i;
            int ty = y + j;

            if (cur[i][j] && (tx <= 0 || tx >= 21 || ty <= 0 || ty >= 11))
                return false;
            if (cur[i][j] && copyMap[tx][ty])            //有方块重合
                return false;
        }
    return true;
}

void AI::addTomap(int x, int y) {

    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++) {

            int tx = x + i;
            int ty = y + j;

            if (tx < 1 || tx > 20 || ty < 1 || ty > 10)    //界外不用考虑，剪枝
                continue;
            if (cur[i][j])
                map[tx][ty] = cur[i][j];
        }
    int maxHeight = 21 - x;
    bool flag = true;
    for (int i = 0; i < 4 && flag; i++)
        for (int j = 0; j < 4; j++) {
            if (cur[i][j])
            {
                maxHeight -= i; flag = false; break;
            }
        }
    int minHeight = 18 - x;
    flag = true;
    for (int i = 3; i >= 0 && flag; i--)
        for (int j = 0; j < 4; j++) {
            if (cur[i][j])
            {
                minHeight += (3 - i); flag = false; break;
            }
        }
    LandingHeight = (maxHeight + minHeight) / 2;
    int cnt = 0, sum = 0;
    for (int i = 20; i >= 1; i--)
    {
        bool flag = false;
        for (int j = 1; j <= 10; j++)
            if (!map[i][j])
            {
                flag = true;
                break;
            }
        if (flag)
            continue;
        cnt++;                                    //行数

        for (int ii = 1; ii <= 10; ii++)
            if (map[i][ii] == '#') sum++;        //计算被消去个数

        for (int j = i; j > 1; j--)
            for (int k = 1; k <= 10; k++)
                map[j][k] = map[j - 1][k];
        i++;
    }

    RowsEliminated = cnt * sum;
}

int AI::RowTransitions() {
    int cnt = 0;
    for (int i = 1; i <= 20; i++)
        for (int j = 0; j <= 10; j++) {
            if (!(map[i][j] && map[i][j + 1]) && (map[i][j] || map[i][j + 1]))
                cnt++;
        }
    return cnt;
}

int AI::ColumnTransitions() {
    int cnt = 0;
    for (int j = 1; j <= 10; j++)
        for (int i = 0; i <= 20; i++) {
            if (!(map[i][j] && map[i + 1][j]) && (map[i][j] || map[i + 1][j]))
                cnt++;
        }
    return cnt;
}

int AI::NumberofHoles() {
    int cnt = 0;
    bool flag;
    for (int j = 1; j <= 10; j++) {
        flag = false;
        for (int i = 1; i <= 20; i++) {
            if (map[i][j]) flag = true;
            else
                if (flag == true) {
                    cnt++;
                    flag = false;
                }
        }
    }
    return cnt;
}

int AI::WellSums() {
    int sum = 0, cnt = 0;
    int well[21] = { 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105,
        120, 136, 153, 171, 190, 210 };        //井的打表

    for (int j = 1; j <= 10; j++)
        for (int i = 1; i <= 20; i++) {
            if (!map[i][j] && map[i][j - 1] && map[i][j + 1]) {
                cnt++;
            }
            else {
                sum += well[cnt];
                cnt = 0;
            }
        }
    return sum;
}

void AI::AIgo(int& X, int& Y, int& shape) {
    int Next[19] = { 1,0,3,2,5,6,7,4,9,10,11,8,13,14,15,12,17,16,18 };
    int bag[7] = { 2,2,4,4,4,2,1 };  //有几种变形
    int bagID[19] = { 0,0,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,6 };//type对应
    char b[19][5][5] =
    {
        //存方块的数据，先不贴代码了，需要的话联系我的邮箱:originum@126.com
    };

    for (int t = 0; t < bag[bagID[type]]; t++) {     //不同变形枚举
        type = Next[type];
        for (int i = 0; i < 4; i++)                 //得到枚举的方块
            for (int j = 0; j < 4; j++) {
                if (b[type][i][j] == '#')
                    cur[i][j] = 0;
                else
                    cur[i][j] = '#';                //特殊标记。此时形状无意义
            }

        for (int i = 1; i <= 10; i++) {
            if (collisionJudge(1, i)) {              //当在第一行都判段为碰撞时，就是出界，不用考虑,理论上也不考虑方块逼近到最高处的可能性
                int j = 1;
                while (collisionJudge(++j, i));      //当没触底碰撞时，向下移动
                j--;

                for (int ii = 0; ii < 22; ii++)       //更新
                    for (int jj = 0; jj < 12; jj++)
                        map[ii][jj] = copyMap[ii][jj];  //把块加入map

                addTomap(j, i);
                int value = PierreDellacherie();
                if (value > maxValue) {
                    X = j;
                    Y = i;
                    shape = type;
                    maxValue = value;
                }
            }
        }
    }
}

int AI::PierreDellacherie() {

    int score = -45 * LandingHeight + 34 * RowsEliminated -
        32 * RowTransitions() -
        93 * ColumnTransitions() -
        79 * NumberofHoles() -
        34 * WellSums();

    return score;
}
*/
